﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using Framework;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Pipeline;
using UnityEngine;
using UnityEngine.Build.Pipeline;
using Debug = UnityEngine.Debug;

namespace FrameworkEditor
{
    internal enum BuildSettingType
    {
        AllToOne,
        OneToOneNoShare,
        OneToOneShare,
        OneToOneCopy,
        SubAllToOne
    }

    public static class AssetsBuilder
    {
        public static readonly string projAbCfgPath = FrameworkSettings.ProjRootDir + "/Editor/Settings/assetbuildsettings.json";
        public static readonly string frameworkCfgPath = FrameworkSettings.FrameworkRootDir + "/Editor/Settings/assetbuildsettings.json";
        private const string AbManifestName = "allmanifest";
        private const string AbManifestPath = "configs/" + AbManifestName;

        /// <summary>
        ///     构建资源
        /// </summary>
        /// <param name="platformId">平台id,在框架设置中有定义</param>
        /// <param name="forceBuild">是否强制重新构建</param>
        /// <param name="onlyBuildLua">是否只构建lua</param>
        /// <param name="onlyPrintSettings">是否只打印absettings</param>
        /// <param name="resVersion">资源版本号,主要是用来写进可选资源列表中的</param>
        /// <param name="isBuildApp">是否构建整包,如果构建整包,则不需要生成c#的patch</param>
        /// <param name="pipelineId">给可选资源包命名用,保证唯一性</param>
        /// <param name="isSkipBundle"></param>
        /// <returns>
        ///     返回构建结果
        ///     0:成功
        ///     -1:代码正在编译
        ///     -2: 编译lua失败
        ///     -3:构建ab失败,返回空的manifest
        /// </returns>
        public static int StartBuild(int platformId,
                                     bool forceBuild,
                                     bool onlyBuildLua,
                                     bool onlyPrintSettings = false,
                                     bool isSkipBundle = false)
        {
            ClearLog.Clear();
            Debug.Log("开始构建资源,platformName = " + FrameworkSettings.PlatformIdToName(platformId) + " forceBuild = " + forceBuild + " onlyBuildLua = " +
                      onlyBuildLua);
            if (EditorApplication.isCompiling)
            {
                throw new BuildFailedException("正在编译代码,不能开始构建Ab,请稍等! code = -1");
            }

            var curTarget = EditorUtils.GetBuildTargetFromPlatformId(platformId);
            EditorUtils.SwitchToTarget(curTarget);

            var platformName = FrameworkSettings.PlatformIdToName(platformId);
            var buildOptions = BuildAssetBundleOptions.None;
            buildOptions |= BuildAssetBundleOptions.StrictMode;
            // 重建 atlas cache
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                UnityEditor.Sprites.Packer.RebuildAtlasCacheIfNeeded(curTarget, false, UnityEditor.Sprites.Packer.Execution.ForceRegroup);
                stopwatch.Stop();
                Debug.Log("build time elapse for RebuildAtlasCacheIfNeeded： " + stopwatch.Elapsed.TotalSeconds + "s!");
            }

            // var buildRootPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName);
            var buildRootPath = FrameworkSettings.GetAbRootDir(platformId);
            if (forceBuild)
            {
                FileHelper.ClearDir(buildRootPath);
            }

            AllToOne.StartRecord();
            FileHelper.EnsureDir(buildRootPath);
            var settings = AssetBuildSettings.GetSettings(platformName);
            if (onlyPrintSettings)
            {
                PrintAbBuildSettings(settings);
                return 0;
            }

            // build lua
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     var ret = CompileLuaToCodes(buildRootPath, platformId, false);
            //     if (ret != 0)
            //     {
            //         throw new BuildFailedException($"编译字节码失败, build_res.log! code = {ret}");
            //     }
            //
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for CompileLuaToCodes： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            // collect excel config
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     BuildExcelConfig.CollectExcelConfig(platformName, settings);
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for BuildExcelConfig.CollectExcelConfig： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            // build lang config
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     BuildLangConfig.Build(platformName);
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for BuildLangConfig.Build： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            if (onlyBuildLua)
            {
                return 0;
            }
            
            // 不打包,只拷贝原始资源
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                foreach (var bundleBuild in settings.copyAssetsSettings)
                {
                    var targetFileName = $"{buildRootPath}/{bundleBuild.assetBundleName}";
                    FileHelper.EnsureDirForFile(targetFileName);
                    File.Copy(bundleBuild.assetNames[0], targetFileName, true);
                }

                stopwatch.Stop();
                Debug.Log("build time elapse for settings.copyAssetsSettings： " + stopwatch.Elapsed.TotalSeconds + "s!");
            }

            //拷贝音效资源,特殊处理
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     _CopyAudioRes(buildRootPath, platformName);
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for _CopyAudioRes： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            //拷贝视频资源,特殊处理
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     _CopyVideoRes(buildRootPath, platformName);
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for _CopyVideoRes： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            AssetDatabase.Refresh();

            if (!isSkipBundle)
            {
                var abBuildSettings = settings.GetAssetBundleBuilds();
                object manifestObj;
                if (!FrameworkSettings.UseSBP)
                {
                    // 旧版本Ab资源打包
                    var stopwatch = new Stopwatch();
                    stopwatch.Start();
                    manifestObj = _DoLegacyAbBuild(platformName, buildRootPath, abBuildSettings, buildOptions, curTarget);
                    stopwatch.Stop();
                    Debug.Log("build time elapse for _DoSBPAbBuild： " + stopwatch.Elapsed.TotalSeconds + "s!");
                }
                else
                {
                    // 新版本Ab资源打包
                    var stopwatch = new Stopwatch();
                    stopwatch.Start();
                    manifestObj = _DoSBPAbBuild(platformName, buildRootPath, abBuildSettings, buildOptions, curTarget);
                    stopwatch.Stop();
                    Debug.Log("build time elapse for _DoSBPAbBuild： " + stopwatch.Elapsed.TotalSeconds + "s!");
                }

                // 资源加密
                // {
                //     var stopwatch = new Stopwatch();
                //     stopwatch.Start();
                //     AssetBundlesEncryptTool.EncryptAb(platformId);
                //     stopwatch.Stop();
                //     Debug.Log("build time elapse for AssetBundlesEncryptTool.EncryptAb： " + stopwatch.Elapsed.TotalSeconds + "s!");
                // }

                // generate all to one ab
                AllToOne.FlushAssetName2AbNameDict(platformName);
                
                var lookTab = new Dictionary<string, string[][]>();
                var configRootPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName);
                var manifestPath = configRootPath + "/" + AbManifestPath + FrameworkSettings.DataExt;
                var manifestBackPath = buildRootPath + "_backup/" + AbManifestName + ".dat";
                var manifestBackJsonPath = buildRootPath + "_backup/" + AbManifestName + ".json";

                // generate lookup table
                {
                    var stopwatch = new Stopwatch();
                    stopwatch.Start();

                    if (!FrameworkSettings.UseSBP)
                    {
                        var manifest = (AssetBundleManifest)manifestObj;
                        foreach (var ab in manifest.GetAllAssetBundles())
                        {
                            lookTab[ab] = new[] { manifest.GetAllDependencies(ab), manifest.GetDirectDependencies(ab) };
                        }
                    }
                    else
                    {
                        var manifest = (CompatibilityAssetBundleManifest)manifestObj;
                        foreach (var ab in manifest.GetAllAssetBundles())
                        {
                            lookTab[ab] = new[] { manifest.GetAllDependencies(ab), manifest.GetDirectDependencies(ab) };
                        }
                    }

                    stopwatch.Stop();
                    Debug.Log("build time elapse for generate lookup table： " + stopwatch.Elapsed.TotalSeconds + "s!");
                }

                // DeleteGMABForRelease
                // {
                //     var stopwatch = new Stopwatch();
                //     stopwatch.Start();
                //     BuildTools.DeleteGMABForRelease(platformId, !isFromCi || CiBuilder.IsDebugBuild());
                //     stopwatch.Stop();
                //     Debug.Log("build time elapse for DeleteGMABForRelease： " + stopwatch.Elapsed.TotalSeconds + "s!");
                // }

                // delete old files
                FileHelper.DeleteFile(manifestBackPath);
                FileHelper.DeleteFile(manifestBackJsonPath);
                FileHelper.DeleteFile(manifestPath);

                // write manifestBackJsonPath
                FileHelper.WriteTextToPath(manifestBackJsonPath, JsonConvert.SerializeObject(lookTab, Formatting.Indented));

                // write manifestBackPath
                using (var fileStream = new FileStream(manifestBackPath, FileMode.CreateNew, FileAccess.Write, FileShare.Write, 20480))
                {
                    using (var binaryWriter = new BinaryWriter(fileStream))
                    {
                        foreach (var kvp in lookTab)
                        {
                            binaryWriter.Write(kvp.Key);
                            var allDeps = kvp.Value[0];
                            binaryWriter.Write(allDeps.Length);
                            foreach (var item in allDeps)
                            {
                                binaryWriter.Write(item);
                            }

                            var directDeps = kvp.Value[1];
                            binaryWriter.Write(directDeps.Length);
                            foreach (var item in directDeps)
                            {
                                binaryWriter.Write(item);
                            }
                        }
                    }
                }

                // write manifestPath
                var data = FileHelper.ReadBytes(manifestBackPath);
                data = UnityZipUtil.ZipBuffer(data);
                FileHelper.WriteAllBytesToPath(manifestPath, data);
            }

            // res split
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     ResSplitTool.Split(splitType, isBuildApp, platformName, resVersion, pipelineId, ResSplitTool.DefaultMaxZipSize,
            //                        !isFromCi || CiBuilder.IsDebugBuild());
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for ResSplitTool.Split： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            // update key
            // {
            //     var stopwatch = new Stopwatch();
            //     stopwatch.Start();
            //     ResInfoTool.UpdateKey(platformName, pipelineId);
            //     stopwatch.Stop();
            //     Debug.Log("build time elapse for ResInfoTool.UpdateKey： " + stopwatch.Elapsed.TotalSeconds + "s!");
            // }

            return 0;
        }

        public static AssetBundleManifest _DoLegacyAbBuild(string platformName,
                                                           string buildRootPath,
                                                           AssetBundleBuild[] abBuildSettings,
                                                           BuildAssetBundleOptions buildOptions,
                                                           BuildTarget curTarget)
        {
            Debug.Log("开始旧版构建ab资源!");
            var abDirPath = buildRootPath;
            FileHelper.EnsureDir(abDirPath);
            var manifest = BuildPipeline.BuildAssetBundles(abDirPath, abBuildSettings, buildOptions, curTarget);
            if (null == manifest)
            {
                throw new BuildFailedException("构建ab失败,manifest返回是空值!code = -3");
            }

            return manifest;
        }

        public static CompatibilityAssetBundleManifest _DoSBPAbBuild(string platformName,
                                                                     string buildRootPath,
                                                                     AssetBundleBuild[] abBuildSettings,
                                                                     BuildAssetBundleOptions buildOptions,
                                                                     BuildTarget curTarget)
        {
            Debug.Log("开始SBP构建ab资源!");
            var abDirPath = buildRootPath;
            FileHelper.EnsureDir(abDirPath);

            var manifest = CompatibilityBuildPipeline.BuildAssetBundles(abDirPath, abBuildSettings, buildOptions, curTarget);
            if (null == manifest)
            {
                throw new BuildFailedException("构建ab失败,manifest返回是空值!code = -3");
            }

            var backBuildlogtepPath = abDirPath + "/../../" + platformName + "_buildlogtep.json";
            FileHelper.DeleteFile(backBuildlogtepPath);
            FileHelper.MoveFile(abDirPath + "/buildlogtep.json", backBuildlogtepPath);
            FileHelper.DeleteFile(abDirPath + "/buildlogtep.json");

            return manifest;
        }

        public static void PrintAbBuildSettings(AssetBuildSettings settings)
        {
            if (null == settings)
            {
                return;
            }

            var stringBuilder = new StringBuilder();
            var abTab = new Dictionary<string, AssetBundleBuild>();
            foreach (var item in settings.GetAssetBundleBuilds())
            {
                var resPath = FrameworkSettings.GetEditorResPath(item.assetBundleName);
                abTab.Add(resPath, item);
            }

            Debug.Log($"PrintAbBuildSettings, all ab count = {abTab.Count} !");
            foreach (var item in abTab)
            {
                var deps = AssetDatabase.GetDependencies(item.Key.Replace(FrameworkSettings.AssetBundleExt, ""));
                stringBuilder.AppendLine("assetBundleName: " + item.Value.assetBundleName);
                stringBuilder.AppendLine("deps:");

                foreach (var dep in deps)
                {
                    if (dep.EndsWith(".cs"))
                    {
                        continue;
                    }

                    if (abTab.ContainsKey(dep + FrameworkSettings.AssetBundleExt))
                    {
                        stringBuilder.AppendLine("  independ:" + dep);
                    }
                }

                foreach (var dep in item.Value.assetNames)
                {
                    stringBuilder.AppendLine("  " + dep);
                }

                stringBuilder.AppendLine();
            }

            Debug.Log("_PrintAbBuildSettings = " + stringBuilder);
            FileHelper.WriteTextToPath(Application.dataPath.Replace("Assets", "abSetttings.txt"), stringBuilder.ToString());
            stringBuilder.Clear();
        }

        [MenuItem("SLTools/AssetsBuilder/BuildLuaBytes[字节码自定义序列化方案] V2")]
        private static void BuildLuaV2()
        {
            var platformName = FrameworkSettings.PlatformIdToName(FrameworkSettings.AndroidPlayer);
            var buildRootPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName);

            // build lua
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                var ret = CompileLuaToCodes(buildRootPath, FrameworkSettings.AndroidPlayer);
                if (ret != 0)
                {
                    Debug.LogError($"编译字节码失败! exit_code={ret}");
                }

                stopwatch.Stop();
                Debug.Log("build time elapse for CompileLuaToCodes： " + stopwatch.Elapsed.TotalSeconds + "s!");
            }
        }

        /// <summary>
        ///     将lua代码编译成字节码
        /// </summary>
        public static int CompileLuaToCodes(string buildRootPath, int platformId, bool isFromCi = false)
        {
            try
            {
                var exitCode = 0;
                const int maxRetryCount = 5;
                for (var i = 1; i <= maxRetryCount; ++i)
                {
                    exitCode = CompileLuaCodeV2.CompileLuaToBytes(buildRootPath, platformId, isFromCi);
                    if (exitCode == -1073741502)
                    {
                        Debug.LogError($"打包lua代码发生异常: 休眠5s后尝试重试. 重试次数={i}/{maxRetryCount}");
                        Thread.Sleep(5000);
                        continue;
                    }

                    return exitCode;
                }
                return exitCode;
            }
            catch (Exception ex)
            {
                Debug.LogError($"打包lua代码发生无法处理的异常: {ex}");
                return -2;
            }

            return 0;
        }

        public static void _CopyAudioRes(string resRootPath, string platformName)
        {
            // string bankDirPath = FrameworkSettings.GetEditorResPath("audios/" + platformName);
            var audiosPath = FileHelper.GetUnityPath(Application.dataPath + "/../../audios/");
            var bankDirPath = FileHelper.GetUnityPath(audiosPath + platformName);
            FileHelper.ClearDir(resRootPath + "/audios/" + platformName);
            if (FileHelper.IsDirExists(bankDirPath))
            {
                foreach (var file in Directory.GetFiles(bankDirPath, "*.*", SearchOption.AllDirectories))
                {
                    if (file.EndsWith(".bnk") || file.EndsWith(".wem"))
                    {
                        var srcPath = FileHelper.GetUnityPath(file);
                        var dstPath = resRootPath + "/audios/" + srcPath.Replace(audiosPath, "");
                        FileHelper.EnsureDirForFile(dstPath);
                        // Debug.Log("srcPath = " + srcPath + " dstPath = " + dstPath);
                        File.Copy(srcPath, dstPath, true);
                    }
                }
            }
        }

        /// <summary>
        ///     android 使用1920*800的。ios使用2592*1080的。pc下的开场视频做了2k和4k的适配
        /// </summary>
        /// <param name="resRootPath"></param>
        /// <param name="platformName"></param>
        public static void _CopyVideoRes(string resRootPath, string platformName)
        {
            // string mp4Path = FrameworkSettings.GetEditorResPath("videos");
            var mp4Path = FileHelper.GetUnityPath(Application.dataPath + "/../../videos");
            var isAndroid = platformName.Equals(FrameworkSettings.Android);
            var hdPath = mp4Path + "/optional/mobile_hd";
            if (platformName.Equals(FrameworkSettings.Windows))
            {
                hdPath = mp4Path + "/optional/pc_hd";
            }

            FileHelper.ClearDir(resRootPath + "/videos");
            if (isAndroid)
            {
                mp4Path = mp4Path + "/low_res";
            }
            else
            {
                mp4Path = mp4Path + "/medium_res";
            }

            if (FileHelper.IsDirExists(mp4Path))
            {
                foreach (var file in Directory.GetFiles(mp4Path, "*.*", SearchOption.AllDirectories))
                {
                    if (file.EndsWith(".mp4"))
                    {
                        var srcPath = FileHelper.GetUnityPath(file);
                        if (srcPath.Contains(hdPath))
                        {
                            continue;
                        }

                        var dstPath = resRootPath + "/videos" + srcPath.Replace(mp4Path, "");
                        // Debug.Log("srcPath = " + srcPath + " dstPath = " + dstPath);
                        FileHelper.EnsureDirForFile(dstPath);
                        File.Copy(srcPath, dstPath, true);
                    }
                }
            }

            if (FileHelper.IsDirExists(hdPath))
            {
                var hdDestPath = resRootPath + "/videos/HD";
                FileHelper.EnsureDir(hdDestPath);
                FileHelper.CopyDirectory(hdPath, hdDestPath);
            }
        }
    }

    public sealed class AssetBuildSettings
    {
        public List<AssetBundleBuild> abSettings;
        internal List<AssetBundleBuild> copyAssetsSettings;

        /// <summary>
        ///     excel导出到json如果压缩成一个json的话,这里配置json的路径
        /// </summary>
        internal string excelDataDir;

        internal static AssetBuildSettings GetSettings(string platformName)
        {
            if (!FileHelper.IsFileExists(AssetsBuilder.projAbCfgPath))
            {
                Debug.LogWarning("项目下没有配置资源构建策略,拷贝框架中的模板!");
                FileHelper.WriteTextToPath(AssetsBuilder.projAbCfgPath, FileHelper.ReadText(AssetsBuilder.frameworkCfgPath));
                AssetDatabase.Refresh();
            }

            var settingText = FileHelper.ReadText(AssetsBuilder.projAbCfgPath);
            if (string.IsNullOrEmpty(settingText))
            {
                Debug.LogError("AssetsBuilder.LoadSettings, settingText is null!");
                return null;
            }

            var ret = new AssetBuildSettings();
            Debug.Log("AssetsBuilder.LoadSettings, settingText = " + settingText);
            var allSettingsTab = JsonHelper.DerializeAsDic(settingText);
            ret.excelDataDir = (string)allSettingsTab["excelConfigDir"];
            ret.abSettings = new List<AssetBundleBuild>();
            ret.copyAssetsSettings = new List<AssetBundleBuild>();

            var abSettingsDic = (Dictionary<string, object>)allSettingsTab["abSettings"];
            AnalysisAbSettings(ret, abSettingsDic);
            
            ret.abSettings.AddRange(GetCommonAbSettings());
            // var singlebgAbCfg = GetSingleBgAbSettings();
            // ret.abSettings.AddRange(singlebgAbCfg);
            // BuildSingleBgAbCfg(platformName, singlebgAbCfg);
            return ret;
        }

        /// <summary>
        ///     获取构建ab设置,去掉重复的项
        /// </summary>
        /// <returns></returns>
        internal AssetBundleBuild[] GetAssetBundleBuilds()
        {
            var abNames = new HashSet<string>();
            var ret = new List<AssetBundleBuild>();
            foreach (var item in abSettings)
            {
                if (!abNames.Add(item.assetBundleName))
                {
                    Debug.LogWarning($"{item.assetBundleName} already added! skip!");
                    continue;
                }

                ret.Add(item);
            }

            return ret.ToArray();
        }

        /// <summary>
        ///     获取excel导出的json配置路径
        /// </summary>
        internal static string GetExcelDataDir()
        {
            var settingText = FileHelper.ReadText(AssetsBuilder.projAbCfgPath);
            if (string.IsNullOrEmpty(settingText))
            {
                Debug.LogError("AssetsBuilder.LoadSettings, settingText is null!");
                return null;
            }

            var allSettingsTab = JsonHelper.DerializeAsDic(settingText);
            var excelConfigDir = (string)allSettingsTab["excelConfigDir"];
            return excelConfigDir;
        }

        public static void AnalysisAbSettings(AssetBuildSettings ret, Dictionary<string, object> settings, bool supportMultiLang = false)
        {
            foreach (var kvp in settings)
            {
                var cfgArgs = (List<object>)kvp.Value;
                if (cfgArgs.Count < 2)
                {
                    Debug.LogError("AssetsBuilder.LoadSettings, parse settiung failed, setting = " + kvp.Key);
                    continue;
                }

                var type = (string)cfgArgs[0];
                var regex = (string)cfgArgs[1];
                var ignoreRegex = cfgArgs.Count >= 3 ? (string)cfgArgs[2] : "";
                var setting = GetAbSettings(kvp.Key, type, regex, ignoreRegex);
                IabSettingAddToAssetBuildSettings(ret, setting);
            }
        }

        private static void IabSettingAddToAssetBuildSettings(AssetBuildSettings ret, IAbSetting setting)
        {
            if (null == setting)
            {
                return;
            }

            if (setting is OneToOneCopy)
            {
                ret.copyAssetsSettings.AddRange(setting.GetAllBuilders());
                return;
            }

            ret.abSettings.AddRange(setting.GetAllBuilders());
        }

        private static IAbSetting GetAbSettings(string path, string type, string regexStr, string ignoreRegexStr)
        {
            Debug.Log("AssetsBuilder.GetAbSettings path = " + path + " type = " + type + " regexStr = " + regexStr);
            if (!Enum.TryParse(type, out BuildSettingType settingType))
            {
                Debug.LogError("AssetsBuilder.GetAbSettings, type = " + type + " is not defined!");
                return null;
            }

            switch (settingType)
            {
                case BuildSettingType.AllToOne:
                    return new AllToOne(path, regexStr);
                case BuildSettingType.OneToOneNoShare:
                    return new OneToOneNoShare(path, regexStr);
                case BuildSettingType.OneToOneShare:
                    return new OneToOneShare(path, regexStr, ignoreRegexStr);
                case BuildSettingType.OneToOneCopy:
                    return new OneToOneCopy(path, regexStr);
                case BuildSettingType.SubAllToOne:
                    return new SubAllToOne(path, regexStr, ignoreRegexStr);
            }

            return null;
        }
        
        private static List<AssetBundleBuild> GetOneLangAtlasSettings(string srcPath)
        {
            var ret = new List<AssetBundleBuild>();
            if (!FileHelper.IsDirExists(srcPath))
            {
                return ret;
            }

            var tagSpriteTab = new Dictionary<string, List<string>>();
            foreach (var file in Directory.GetFiles(srcPath, "*.*", SearchOption.AllDirectories))
            {
                if (!file.EndsWith(".png"))
                {
                    continue;
                }

                var unityPath = FileHelper.GetUnityPath(file);
                var textureImporter = AssetImporter.GetAtPath(unityPath) as TextureImporter;
                if (null != textureImporter)
                {
                    if (string.IsNullOrEmpty(textureImporter.spritePackingTag))
                    {
                        Debug.LogError("没有给图集资源设置tag,请检查!path = " + unityPath);
                        continue;
                    }

                    if (tagSpriteTab.TryGetValue(textureImporter.spritePackingTag, out var spriteArr))
                    {
                        spriteArr.Add(unityPath);
                    }
                    else
                    {
                        spriteArr = new List<string> { unityPath };
                        tagSpriteTab[textureImporter.spritePackingTag] = spriteArr;
                    }
                }
            }

            foreach (var item in tagSpriteTab)
            {
                var abb = new AssetBundleBuild
                {
                    assetBundleName = GetAbNameByTag(item.Key),
                    assetNames = item.Value.ToArray()
                };
                Debug.Log($"tag = {item.Key} name = {abb.assetBundleName}");
                ret.Add(abb);
            }

            return ret;
        }

        private static string GetAbNameByTag(string tag)
        {
            var lastIdx = tag.LastIndexOf('_');
            var langShortcut = tag.Substring(lastIdx + 1);
            if (LanguageHelper.IsLangIsDefined(langShortcut))
            {
                return LanguageHelper.GetLangResRootDir(langShortcut) + "/atlas/" + tag + FrameworkSettings.AssetBundleExt;
            }

            return "atlas/" + tag + FrameworkSettings.AssetBundleExt;
        }

        /// <summary>
        ///     ui/viewres, effects prefab抽公共图片打包到一个ab中，减少内存冗余
        /// </summary>
        /// <returns></returns>
        public static List<AssetBundleBuild> GetCommonAbSettings()
        {
            var ret = new List<AssetBundleBuild>();
            var settingText = FileHelper.ReadText(FilterCommonAbTool.abCommonResultPath);
            if (string.IsNullOrEmpty(settingText))
            {
                Debug.LogWarning("AssetsBuilder.GetCommonAbSettings, settingText is null!");
                return ret;
            }

            var settingsTab = JsonHelper.DerializeAsDic(settingText);
            var assetNames = new List<string>();
            foreach (var item in settingsTab)
            {
                var newAbb = new AssetBundleBuild
                {
                    assetBundleName = EditorUtils.GetRelativePathToDir(item.Key, EditorUtils.assetRootDir) + FrameworkSettings.AssetBundleExt
                };
                assetNames.Clear();
                var oneDirTab = (Dictionary<string, object>)item.Value;
                foreach (var assetPath in oneDirTab.Keys)
                {
                    var filePath = Application.dataPath.Replace("Assets", assetPath);
                    if (!FileHelper.IsFileExists(filePath))
                    {
                        Debug.LogWarning("AssetsBuilder.GetCommonAbSettings, filePath = " + filePath + " is not exist!");
                        continue;
                    }

                    assetNames.Add(assetPath);
                }

                newAbb.assetNames = assetNames.ToArray();
                ret.Add(newAbb);
            }

            return ret;
        }

        // [MenuItem("Test/PrintSinglebgAbSetting")]
        public static void TestSingleBgAvSetting()
        {
            var settings = GetSingleBgAbSettings();
            var tab = new Dictionary<string, string[]>();
            foreach (var item in settings)
            {
                if (tab.ContainsKey(item.assetBundleName))
                {
                    Debug.LogError($"{item.assetBundleName} already exists");
                }

                tab.Add(item.assetBundleName, item.assetNames);
            }

            var jsonStr = JsonConvert.SerializeObject(tab, Formatting.Indented);
            Debug.Log(jsonStr);
            FileHelper.WriteTextToPath(Application.dataPath.Replace("Assets", "singlebgAbSettings.json"), jsonStr);
        }

        /// <summary>
        ///     singlebg打包策略按照图大小均摊到ab中,减少ab数量,同时减低内存占用
        /// </summary>
        /// <returns></returns>
        public static List<AssetBundleBuild> GetSingleBgAbSettings()
        {
            // "Assets/ZResourcesLib/singlebg": [
            // 	"OneToOneNoShare",
            // 	"(.png|.jpg)$"
            // ],
            var singleBgBuildCfg = new OneToOneNoShare("Assets/Res/singlebg", "(.png|.jpg)$");
            // 一对一的ab
            var lagacyCfg = singleBgBuildCfg.GetAllBuilders();
            Debug.Log($"AssetsBuilder.GetSingleBgAbSettings, lagacyCfg count = {lagacyCfg.Count}");
            var abCfgJson = FileHelper.ReadText(SinglebgAbSettingTool.singleBgAbCfgPath);
            if (string.IsNullOrEmpty(abCfgJson))
            {
                Debug.LogError($"AssetsBuilder.GetSingleBgAbSettings, singlebg ab config is not exist : {SinglebgAbSettingTool.singleBgAbCfgPath}, please generate it!");
                return lagacyCfg;
            }

            var nowAbCfg = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(abCfgJson);
            var ret = new List<AssetBundleBuild>();
            var existedAssetNames = new List<string>();
            // 根据配置，把多个image合到一个ab中了。
            foreach (var item in nowAbCfg)
            {
                existedAssetNames.Clear();
                foreach (var assetName in item.Value)
                {
                    if (FileHelper.IsFileExists(assetName))
                    {
                        existedAssetNames.Add(assetName);
                    }
                }

                //如果这个ab对应的asset都被删除了，则不构建这个公共ab
                if (existedAssetNames.Count != 0)
                {
                    ret.Add(new AssetBundleBuild
                    {
                        assetBundleName = item.Key + FrameworkSettings.AssetBundleExt,
                        assetNames = existedAssetNames.ToArray()
                    });
                }

                // 被合并了的单独打包，需要删掉单独的
                foreach (var assetName in item.Value)
                {
                    var lagacyAbName = EditorUtils.GetRelativePathToDir(assetName, EditorUtils.assetRootDir) + FrameworkSettings.AssetBundleExt;
                    // Debug.Log($"assetName = {assetName} lagacyAbName = {lagacyAbName}");
                    for (var idx = lagacyCfg.Count - 1; idx >= 0; idx--)
                    {
                        if (lagacyAbName.Equals(lagacyCfg[idx].assetBundleName))
                        {
                            // Debug.Log($"lagacyCfg remove assetName = {assetName} lagacyAbName = {lagacyAbName}");
                            lagacyCfg.RemoveAt(idx);
                        }
                    }
                }
            }

            var log = $"lagacyCfg left count = {lagacyCfg.Count}, abnames:\n";
            foreach (var item in lagacyCfg)
            {
                ret.Add(item);
                log += item.assetBundleName + "\n";
            }

            Debug.Log("AssetsBuilder.GetSingleBgAbSettings," + log);
            return ret;
        }

        public static void BuildSingleBgAbCfg(string platformName, List<AssetBundleBuild> abCfg)
        {
            var abTab = new Dictionary<string, string[]>();
            foreach (var item in abCfg)
            {
                abTab.Add(item.assetBundleName, item.assetNames);
            }

            var abTabJson = JsonConvert.SerializeObject(abTab, Formatting.Indented);
            var cfgData = UnityZipUtil.ZipStr(abTabJson);
            var buildRootPath = Application.dataPath.Replace("Assets", FrameworkSettings.OutResLibName + "/" + platformName);
            FileHelper.WriteAllBytesToPath(buildRootPath + "/configs/singlebgabtab.dat", cfgData);
            var backupDir = $"{Application.dataPath}/../ResLib/" + platformName + "_backup";
            FileHelper.WriteTextToPath(backupDir + "/singlebgabtab.json", abTabJson);
        }
    }
}